001 /*
002 * Copyright 2003-2005 The Apache Software Foundation
003 * Copyright 2005 Stephen McConnell
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package net.dpml.cli.option;
018
019 import java.util.Iterator;
020 import java.util.ListIterator;
021 import java.util.Set;
022
023 import net.dpml.cli.DisplaySetting;
024 import net.dpml.cli.Option;
025 import net.dpml.cli.WriteableCommandLine;
026 import net.dpml.cli.resource.ResourceConstants;
027 import net.dpml.cli.resource.ResourceHelper;
028
029 /**
030 * A base implementation of Option providing limited ground work for further
031 * Option implementations.
032 * @author <a href="http://www.dpml.net">Digital Product Meta Library</a>
033 * @version 1.0.0
034 */
035 public abstract class OptionImpl implements Option
036 {
037 private final int m_id;
038 private final boolean m_required;
039
040 /**
041 * Creates an OptionImpl with the specified id
042 * @param id the unique id of this Option
043 * @param required true iff this Option must be present
044 */
045 public OptionImpl( final int id, final boolean required )
046 {
047 m_id = id;
048 m_required = required;
049 }
050
051 /**
052 * Indicates whether this Option will be able to process the particular
053 * argument. The ListIterator must be restored to the initial state before
054 * returning the boolean.
055 *
056 * @see #canProcess(WriteableCommandLine,String)
057 * @param commandLine the CommandLine object to store defaults in
058 * @param arguments the ListIterator over String arguments
059 * @return true if the argument can be processed by this Option
060 */
061 public boolean canProcess(
062 final WriteableCommandLine commandLine, final ListIterator arguments )
063 {
064 if( arguments.hasNext() )
065 {
066 final String argument = (String) arguments.next();
067 arguments.previous();
068 return canProcess( commandLine, argument );
069 }
070 else
071 {
072 return false;
073 }
074 }
075
076 /**
077 * Returns a string representation of the option.
078 * @return the string value
079 */
080 public String toString()
081 {
082 final StringBuffer buffer = new StringBuffer();
083 appendUsage( buffer, DisplaySetting.ALL, null );
084 return buffer.toString();
085 }
086
087 /**
088 * Returns the id of the option. This can be used in a loop and switch
089 * construct:
090 *
091 * <code>
092 * for(Option o : cmd.getOptions()){
093 * switch(o.getId()){
094 * case POTENTIAL_OPTION:
095 * ...
096 * }
097 * }
098 * </code>
099 *
100 * The returned value is not guarenteed to be unique.
101 *
102 * @return the id of the option.
103 */
104 public int getId()
105 {
106 return m_id;
107 }
108
109 /**
110 * Evaluate this instance against the supplied instance for equality.
111 * @param thatObj the other object
112 * @return true if the supplied instance is equal to this instance
113 */
114 public boolean equals( final Object thatObj )
115 {
116 if( thatObj instanceof OptionImpl )
117 {
118 final OptionImpl that = (OptionImpl) thatObj;
119 return ( getId() == that.getId() )
120 && equals( getPreferredName(), that.getPreferredName() )
121 && equals( getDescription(), that.getDescription() )
122 && equals( getPrefixes(), that.getPrefixes() )
123 && equals( getTriggers(), that.getTriggers() );
124 }
125 else
126 {
127 return false;
128 }
129 }
130
131 private boolean equals( Object left, Object right )
132 {
133 if( ( left == null ) && ( right == null ) )
134 {
135 return true;
136 }
137 else if( ( left == null ) || ( right == null ) )
138 {
139 return false;
140 }
141 else
142 {
143 return left.equals( right );
144 }
145 }
146
147 /**
148 * Return the hashcode value for this instance.
149 * @return the hash value
150 */
151 public int hashCode()
152 {
153 int hashCode = getId();
154 hashCode = ( hashCode * 37 ) + getPreferredName().hashCode();
155 if( getDescription() != null )
156 {
157 hashCode = ( hashCode * 37 ) + getDescription().hashCode();
158 }
159 hashCode = ( hashCode * 37 ) + getPrefixes().hashCode();
160 hashCode = ( hashCode * 37 ) + getTriggers().hashCode();
161 return hashCode;
162 }
163
164 /**
165 * Recursively searches for an option with the supplied trigger.
166 *
167 * @param trigger the trigger to search for.
168 * @return the matching option or null.
169 */
170 public Option findOption( String trigger )
171 {
172 if( getTriggers().contains( trigger ) )
173 {
174 return this;
175 }
176 else
177 {
178 return null;
179 }
180 }
181
182 /**
183 * Indicates whether this option is required to be present.
184 * @return true if the CommandLine will be invalid without this Option
185 */
186 public boolean isRequired()
187 {
188 return m_required;
189 }
190
191 /**
192 * Adds defaults to a CommandLine.
193 *
194 * Any defaults for this option are applied as well as the defaults for
195 * any contained options
196 *
197 * @param commandLine the CommandLine object to store defaults in
198 */
199 public void defaults( final WriteableCommandLine commandLine )
200 {
201 // nothing to do normally
202 }
203
204 /**
205 * Check prefixes.
206 * @param prefixes the prefixes set
207 */
208 protected void checkPrefixes( final Set prefixes )
209 {
210 // nothing to do if empty prefix list
211 if( prefixes.isEmpty() )
212 {
213 return;
214 }
215
216 // check preferred name
217 checkPrefix( prefixes, getPreferredName() );
218
219 // check triggers
220 getTriggers();
221
222 for( final Iterator i = getTriggers().iterator(); i.hasNext();)
223 {
224 checkPrefix( prefixes, (String) i.next() );
225 }
226 }
227
228 /**
229 * Check prefixes.
230 * @param prefixes the prefixes set
231 * @param trigger the trigger
232 */
233 private void checkPrefix( final Set prefixes, final String trigger )
234 {
235 for( final Iterator i = prefixes.iterator(); i.hasNext();)
236 {
237 String prefix = (String) i.next();
238 if( trigger.startsWith( prefix ) )
239 {
240 return;
241 }
242 }
243
244 final ResourceHelper helper = ResourceHelper.getResourceHelper();
245 final String message =
246 helper.getMessage(
247 ResourceConstants.OPTION_TRIGGER_NEEDS_PREFIX,
248 trigger,
249 prefixes.toString() );
250 throw new IllegalArgumentException( message );
251 }
252 }